/* $Header: formatter.c,v 2.2 01/10/02 10:26:17 przybyls Exp $ */

/***********************************************************************/
/* MODULE NAME:  formatter.c                                           */
/* ============                                                        */
/*                                                                     */
/* Author:  Susan Merkel                                               */
/*                                                                     */
/* MODULE PURPOSE:  This module contains functions for general output  */
/*                  formatting into a character buffer.                */
/*                                                                     */
/***********************************************************************/
/*               Copyright Hewlett-Packard Co. 1991-1992               */
/***********************************************************************/

/*****************************/
/*   GLOBAL INCLUDE FILES    */
/*****************************/

#include  <typedefs.h>
#include  <p_wkfile.h>
#include  <config.h>
#include  <wkfile.h>
#include  <ramps.h>
#include  <zone_config.h>
#include  <zone_status.h>
#include  <status.h>
#include  <display.h>
#include  <error.h>
#include  <p_config.h>
#include  <p_status.h>
#include  <p_ui.h>
#include  <msg.h>

/*****************************/
/*   IMPLEMENT SECTION       */
/*****************************/

#define MAXPOSLONG	0x7fffffff	/* biggest INT32 */

/***********************************************************************/
/* FUNCTION:  FormatDecimal                                            */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:  This function formats a decimal number and outputs the    */
/*           number into a character buffer.                           */
/*                                                                     */
/*           INPUTS:                                                   */
/*             buffer - pointer to string to format number into        */
/*             number - INT32 to be put into decimal format            */
/*             startloc - position in string for start of number       */
/*             width - field width for output format; if positive      */
/*                     then blank-fill, if "negative" then zero-fill   */
/*             scale - number of digits in number after the radix;     */
/*                     very large values are used to trigger special   */
/*                     case formats for pressure display, etc.         */
/*                                                                     */
/* RETURNED VALUE:  buffptr - a pointer to the remaining portion of    */
/*                  the character buffer (the location after the       */
/*                  formatted number.) This is to enable further       */
/*                  building of the output string.                     */
/*                                                                     */
/* REVISIT: May want to rewrite this to input a buffer                 */
/*          pointer rather than a starting location, so you could      */
/*          call this routine with the buffptr it returns...           */
/*                                                                     */
/***********************************************************************/

BIT8 *FormatDecimal(BIT8 * buffer, INT32 number, U_INT8 startloc,
	     U_INT8 width, U_INT8 scale)
{
	INT32 maxfieldnum;
	int places;
	int i;
	int bufferpos;
	int neg = FALSE;
	int zfmt = FALSE;
	int overflow = FALSE;

	/* is zero-fill format requested? */
	if (width > 127)
	{
		zfmt = TRUE;
		width = 256 - width;  /* replace with positive equivalent */
	}

	/* is variable format for pressure requested? */
	if (scale == TAG_PRESSURE)
	{
		switch (GetPresUnits())
		{
		case DISPLAY_BAR:
			scale = 2;
			break;

		case DISPLAY_PSI:
			scale = 1;
			break;

		case DISPLAY_KPA:
		default:  /* assert: default should never occur */
			scale = 0;
		}
	}

	/* is variable format for pressure rate requested? */
	else if (scale == TAG_PRESSURE_RATE)
	{
GetColumn1Ptr();
		switch (GetPresUnits())
		{
		case DISPLAY_BAR:
			scale = 3;
			break;

		case DISPLAY_PSI:
			scale = 2;
			break;

		case DISPLAY_KPA:
		default:  /* assert: default should never occur */
			scale = 1;
		}
	}

	/* Is number negative? */
	if (number < 0)
	{
		neg = TRUE;

		/* Negate because formatting of unsigned number is easier.
		   Clamp to -MAXPOSLONG if negation would overflow. */
		if (number < -MAXPOSLONG)
		{
			number = MAXPOSLONG;
		}
		else
		{
			number = -number;
		}
	}

	/* Set bufferpos to end of width and format from end. */
	bufferpos = startloc + width - 1;

	/* calculate # of digits available to format number */
	places = (neg) ? (width - 1) : width;

	/* calculate largest number that could fit in the field width */
	if (places >= 10)
	{
		/* the biggest 32 bit number requires a 10 digit field */
		maxfieldnum = MAXPOSLONG;
	}
	else
	{
		maxfieldnum = 1;
		for (i = 1; i <= places; i++)
		{
			maxfieldnum *= 10;
		}
		maxfieldnum--;
	}

	/* make number fit width by reducing # of digits after radix if necessary */
	if (scale > 0)
	{
		while ((number > (maxfieldnum / 10)) && (scale > 0))
		{
			number /= 10;
			scale--;
		}

		/* if number won't fit, even at scale 0, we have overflow */
		if (scale == 0 && number > maxfieldnum)
		{
			overflow = TRUE;
		}
	}
	else  /* scale == 0 */
	{
		/* if number won't fit, we have overflow */
		if (number > maxfieldnum)
		{
			overflow = TRUE;
		}
	}

	/* we are finally ready to start outputting characters */
	if (overflow)
	{
		/* fill field with overflow symbol */
		while (bufferpos >= startloc)
			buffer[bufferpos--] = '*';
	}
	else
	{
		if (scale > 0)
		{
			/* Format the digits after the decimal point. */
			for (i = 1; i <= scale; i++)
			{
				buffer[bufferpos--] = (number % 10) + '0';
				number /= 10;
			}

			/* Insert the radix character (decimal point or comma). */
			buffer[bufferpos--] = Radix_Char;
		}

		/* format the digits to the left of the decimal point */
		if (number == 0)
		{
			buffer[bufferpos--] = '0';
		}
		else
		{
			while ((number > 0) && (bufferpos >= startloc))
			{
				buffer[bufferpos--] = (number % 10) + '0';
				number /= 10;
			}
		}

		/* if blank-fill, apply negative sign to the left of the first digit */
		if (neg && !zfmt)
		{
			buffer[bufferpos--] = '-';
		}

		/* If the number does not take up the width, pad left with blanks or zeroes */
		while (bufferpos >= startloc)
		{
			buffer[bufferpos--] = (zfmt) ? '0' : ' ';
		}

		/* if zero-fill, apply negative sign in first position of field */
		if (neg && zfmt)
		{
			buffer[startloc] = '-';
		}
	}

	/* return pointer to the next character position after the formatted number */
	return &(buffer[startloc + width]);
}

/* FormatHexadecimal - format a hex value, return ptr to next char */
BIT8 *FormatHexadecimal (U_INT32 num, BIT8 *string, U_INT8 width)
{
	BIT8 *s = string;
	U_INT32 nibble;
	int i, filling = TRUE;
	enum {ZERO_FILL, BLANK_FILL, MIN_WIDTH} fmt;

	/* format is encoded in width:  < 0 denotes zero-fill,
					> 0 denotes blank-fill,
					= 0 denotes no-fill, i.e. minimum width */
	if (width == 0)
	{
		fmt = MIN_WIDTH;
		width = 8;  /* max 32-bit value needs 8 nibbles for representation */
	}
	else if (width > 127)
	{
		fmt = ZERO_FILL;
		width = 256 - width;  /* replace with positive equivalent */
	}
	else
	{
		fmt = BLANK_FILL;
	}

	/* if width < 8 then left-justify the subset to be formatted */
	if (width < 8)
	{
		num = num << ((8 - width) * 4);
	}

	/* hex format the number */
	for (i = 0; i < width; i++)
	{
		/* extract a nibble (4-bits) */
		nibble = (num & 0xf0000000) >> 28;
		num = num << 4;

		/* filling stops with the first non-zero nibble encountered */
		if (nibble != 0)
		{
			filling = FALSE;
		}

		if (filling)
		{
			/* format insignificant (leading zero) nibbles */
			switch (fmt)
			{
			case BLANK_FILL:
				*s = ' ';
				s++;
				break;

			case ZERO_FILL:
				*s = '0';
				s++;
				break;

			case MIN_WIDTH:
				break; /* perform no filling */

			default:
				break; /* internal error */
			}
		}
		else
		{
			/* format significant nibbles */
			*s = (nibble < 10) ? '0' + nibble : ('A' - 10) + nibble;
			s++;
		}
	}

	/* avoid empty output in BLANK_FILL and MIN_WIDTH cases when (num == 0) */
	switch (fmt)
	{
	case BLANK_FILL:
		if (*(s - 1) == ' ')
		{
			*(s - 1) = '0';
		}
		break;

	case MIN_WIDTH:
		if (s == string)
		{
			*s = '0';
			s++;
		}
		break;
	}

	return s;
}
